package com.springboot.demo.service.impl;

import com.springboot.demo.entity.Menu;
import com.springboot.demo.entity.MenuGroup;
import com.springboot.demo.entity.Role;
import com.springboot.demo.entity.User;
import com.springboot.demo.exception.BusinessException;
import com.springboot.demo.mapper.MenuMapper;
import com.springboot.demo.mapper.RoleMapper;
import com.springboot.demo.mapper.UserMapper;
import com.springboot.demo.service.RoleService;
import com.springboot.demo.service.RoleService;
import com.springboot.demo.service.UserService;
import com.springboot.demo.utils.Constant;
import com.springboot.demo.utils.RedisUtils;
import com.springboot.demo.utils.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author wubc683
 * @desc
 * @date 2021-08-26 9:59
 */
@Service
public class RoleServiceImpl implements RoleService {
    @Autowired
    RoleMapper roleMapper;
    @Autowired
    UserMapper userMapper;
    @Autowired
    MenuMapper menuMapper;
    @Autowired
    UserService userService;
    @Autowired
    RedisUtils redisUtils;
    private static final Integer MAX_ROLE = 50; // 角色最多50个
    private static final Integer MENU_NOT_SELECTED = 0; // 角色菜单没选中

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRole(User implUser, Role role) {
        // 查询用户已有角色数量
        List<Long> roleIdByUid = roleMapper.getRoleIdByUid(implUser.getId());
        if (roleIdByUid.size() >= MAX_ROLE) {
            throw new BusinessException(ResponseResult.ROLE_COUNT_LIMIT);
        }
        // 校验用户是否已经有此角色名
        Role role1 = userMapper.selectUserRoleByUidAndRoleName(implUser.getId(), role.getName());
        if (role1 != null) {
            throw new BusinessException(ResponseResult.ROLE_DUPLICATE);
        }
        roleMapper.saveRole(role);
        userMapper.insertUserRoleByUidAndRid(implUser.getId(), role.getId());
        // 按角色为user的菜单，创建一个模板
        List<MenuGroup> menuGroupByRid = menuMapper.getMenuGroupByRid(Constant.USER);
        menuMapper.insertRoleMenuGroup(role.getId(), menuGroupByRid, MENU_NOT_SELECTED);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRole(User implUser, Long rid) {
        // 系统默认角色不能删除
        if (Constant.SUPER_ADMIN.equals(rid) || Constant.ADMIN.equals(rid) || Constant.USER.equals(rid)) {
            throw new BusinessException(ResponseResult.DEFAULT_ROLE_CANNOT_DELETE);
        }
        // 校验角色id是否有效
        List<Long> userIdByRid = roleMapper.getUserIdByRid(rid);
        if (userIdByRid == null || !userIdByRid.contains(implUser.getId())) {
            throw new BusinessException(ResponseResult.USER_DO_NOT_HAVE_THIS_ROLE);
        }
        // 更新角色对应菜单的缓存
        List<Menu> menuByRid = menuMapper.getMenuByRid(rid, Constant.MENU.SELECTED);
        for (int i = 0; i < menuByRid.size(); i++) {
            redisUtils.setRemove("menu::url::" + menuByRid.get(i).getUrl() + "::method::" + menuByRid.get(i).getMethod(),
                    rid);
        }
        // 将拥有该角色的关联用户角色修改为系统默认user角色
        userMapper.updateRelativeUserRoleByUidAndRid(rid, Constant.USER);
        userMapper.updateRelativeUserByUidAndRid(rid, Constant.USER);
        // 从管理员角色列表中删除此角色
        userMapper.deleteUserRoleByUidAndRid(implUser.getId(), rid);
        // 删除角色
        roleMapper.deleteRoleById(rid);
        // 删除角色包含的菜单
        menuMapper.deleteMenuGroupByRid(rid);
        // 删除redis中缓存
        for (Long id : userIdByRid) {
            redisUtils.delete("user::" + id);
            redisUtils.delete("relativeUser::" + id);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRole(User implUser, Role role) {
        // 系统默认角色不能修改
        if (Constant.SUPER_ADMIN.equals(role.getId()) ||
                Constant.ADMIN.equals(role.getId()) || Constant.USER.equals(role.getId())) {
            throw new BusinessException(ResponseResult.DEFAULT_ROLE_CANNOT_UPDATE);
        }
        // 校验角色id是否有效
        List<Long> userIdByRid = roleMapper.getUserIdByRid(role.getId());
        if (userIdByRid == null || !userIdByRid.contains(implUser.getId())) {
            throw new BusinessException(ResponseResult.USER_DO_NOT_HAVE_THIS_ROLE);
        }
        // 校验角色名是否重复
        Role roleByUidAndRole = roleMapper.getRoleByUidAndRole(implUser.getId(), role.getName());
        if (roleByUidAndRole != null && !roleByUidAndRole.getId().equals(role.getId())) {
            throw new BusinessException(ResponseResult.ROLE_DUPLICATE);
        }
        roleMapper.updateRole(role);
    }

    @Override
    public Role getRole(User implUser, Long id) {
        Role role = roleMapper.getRoleByUidAndRid(implUser.getId(), id);
        if (role == null) {
            throw new BusinessException(ResponseResult.USER_DO_NOT_HAVE_THIS_ROLE);
        }
        return role;
    }

    @Override
    public List<Role> getRoles(User user) {
        return roleMapper.getUserRolesByUserId(user.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRoleMenu(User user, Long rid, List<MenuGroup> menuGroups) {
        // 系统默认角色菜单不能修改
        if (Constant.SUPER_ADMIN.equals(rid) || Constant.ADMIN.equals(rid) || Constant.USER.equals(rid)) {
            throw new BusinessException(ResponseResult.DEFAULT_ROLE_CANNOT_UPDATE);
        }
        // 校验角色id是否有效
        List<Long> userIdByRid = roleMapper.getUserIdByRid(rid);
        if (userIdByRid == null || !userIdByRid.contains(user.getId())) {
            throw new BusinessException(ResponseResult.USER_DO_NOT_HAVE_THIS_ROLE);
        }
        // 检查菜单id是否有效, 和默认分配的菜单对比
        List<MenuGroup> menuGroupByRid = menuMapper.getMenuGroupByRid(Constant.USER);
        Map<Long, MenuGroup> idMenuGroup = new HashMap<>();
        Set<Long> ids = new HashSet<>();
        for (MenuGroup mg : menuGroupByRid) {
            idMenuGroup.put(mg.getId(), mg);
        }
        Map<Long, Boolean> idSelected = new HashMap<>();
        for (MenuGroup m : menuGroups) {
            // 检测菜单id是否存在
            if (idMenuGroup.get(m.getId()) == null) {
                throw new BusinessException(ResponseResult.MENU_NOT_EXISTED);
            }
            if (ids.contains(m.getId())) {
                throw new BusinessException(ResponseResult.MENU_DUPLICATE);
            }
            ids.add(m.getId());
            idSelected.put(m.getId(), m.getSelected());
        }
        // 校验id是否缺少
        if (idSelected.size() < idMenuGroup.size()) {
            throw new BusinessException(ResponseResult.ROLE_LACK_MENU_GROUP);
        }

        // 将selected值转移到数据库中user查出的菜单中
        for (MenuGroup mg : menuGroupByRid) {
            mg.setSelected(idSelected.get(mg.getId()));
        }
        // 校验id的父id，前缀id是否存在,利用数据库中user查出的菜单和前端传的selected值校验
        for (MenuGroup mg : menuGroupByRid) {
            if (mg.getSelected()) {
                if (!mg.getParentId().equals(0L)) {
                    MenuGroup menuGroup = idMenuGroup.get(mg.getParentId());
                    if (menuGroup == null || !menuGroup.getSelected()) {
                        throw new BusinessException(ResponseResult.PARENT_MENU_NOT_SELECTED);
                    }
                }
                if (!mg.getPreMenuId().equals(0L)) {
                    MenuGroup menuGroup = idMenuGroup.get(mg.getPreMenuId());
                    if (menuGroup == null || !menuGroup.getSelected()) {
                        throw new BusinessException(ResponseResult.PRE_MENU_NOT_SELECTED);
                    }
                }
            }
        }
        menuMapper.updateRoleMenuGroups(rid, menuGroups);
        // 更新角色对应菜单的缓存
        List<Menu> menuByRid = menuMapper.getMenuByRid(rid, Constant.MENU.SELECTED);
        for (int i = 0; i < menuByRid.size(); i++) {
            redisUtils.sSet("menu::url::" + menuByRid.get(i).getUrl() + "::method::" + menuByRid.get(i).getMethod(), rid);
        }
    }

//    @Override
//    public List<Menu> getRoleMenu(User user, Long rid) {
//        // 检查角色id是否存在
//        Role role = roleMapper.getRoleById(rid);
//        if (role == null) {
//            throw new BusinessException(ResponseResult.ROLE_NOT_EXISTED);
//        }
//        // 校验用户是否拥有角色id
//        Role roleByUidAndRid = roleMapper.getRoleByUidAndRid(user.getId(), rid);
//        if (roleByUidAndRid == null) {
//            throw new BusinessException(ResponseResult.USER_DO_NOT_HAVE_THIS_ROLE);
//        }
//        List<Menu> menuByRid = menuMapper.getMenuByRid(rid);
//        // 根目录菜单
//        List<Menu> root = new ArrayList<>();
//        for (Menu m : menuByRid) {
//            if (m.getParentId() == null) {
//                root.add(m);
//            }
//        }
//        for (Menu me : root) {
//            me.setChildMenus(getMenuChild(menuByRid, me));
//        }
//        return root;
//    }

//    @Override
//    public List<Menu> getAllRoleMenu() {
//        List<Menu> menuByRid = menuMapper.getAllMenus(Constant.USER);
//        // 根目录菜单
//        List<Menu> root = new ArrayList<>();
//        for (Menu m : menuByRid) {
//            if (m.getParentId() == null) {
//                root.add(m);
//            }
//        }
//        for (Menu me : root) {
//            me.setChildMenus(getMenuChild(menuByRid, me));
//        }
//        return root;
//    }

    @Override
    public List<MenuGroup> getRoleMenuGroup(User user, Long rid) {
        // 校验用户是否拥有角色id
        Long uidByUidAndRid = roleMapper.getUidByUidAndRid(user.getId(), rid);
        if (uidByUidAndRid == null) {
            throw new BusinessException(ResponseResult.USER_DO_NOT_HAVE_THIS_ROLE);
        }
        return menuMapper.getMenuGroupByRid(rid);
    }

//    // 获取菜单树
//    private List<Menu> getMenuChild(List<Menu> menus, Menu menu) {
//        List<Menu> child = new ArrayList<>();
//        for (Menu m : menus) {
//            if (m.getParentId() != null && m.getParentId().equals(menu.getId().intValue())) {
//                child.add(m);
//            }
//        }
//        for (Menu me : child) {
//            me.setChildMenus(getMenuChild(menus, me));
//        }
//        if (child.isEmpty()) {
//            return null;
//        }
//        return child;
//    }
}
